#include "../include/lecteur.hpp"

using namespace std;
FileManager * FileManager::_me = NULL;
const char* Box::nomMnt ="bassins/europetotal.bin";
//const char* Box::nomMnt ="bassins/MNT_LB.bin";
//const char* Box::nomMnt ="bassins/mnt100_sb50.bin";
//const char* Box::nomMnt ="bassins/MNT50m.bin";
//const char* Box::nomMnt ="bassins/bassin11x11.bin";
//const char* Box::nomMnt ="bassins/nigeretendu.bin";

const char* Box::nomPt = "bassin.pt";

const char* Box::nomTmp = "tmp";

FileManager::FileManager()
{
    _me = this;
    _lignes=0;
    _nblig=0;
    _nbcol=0;
    fichiersource = new std::ifstream("bassin.bin", std::ifstream::in | std::ifstream::binary);
    lolilol tip;
    fichiersource->read((char*)&tip, sizeof(tip));
    _nblig=tip.b;
    _nbcol=tip.a;
    _novalue=tip.f;
    ofstream out;
    out.open("tempo",ios_base::out | ios_base::trunc);
    out.close();
    out.open("defrag",ios_base::out | ios_base::trunc);
    out.close();
    out.open("defragfinal",ios_base::out | ios_base::trunc);
    out.close();
}

bool FileManager::getLine(int *tab, unsigned int line, const char * fic)
{
    //on ouvre en lecture seule
    ifstream fichier(fic, ifstream::in | std::ifstream::binary);

    //verifie si le fichier demande a ete trouve
    if(fichier)
    {
        unsigned int cols,rows;
        lolilol tip;
        fichier.read((char*)&tip, sizeof(tip));
        cols = tip.a; rows=tip.b;

        //verifie si on ne demande pas une ligne exterieure au fichier
        if(rows > line)
        {
            //on se place a la bonne ligne dans le mnt
            for(unsigned int i = 0; i < line; i++)
                fichier.read((char*)&tab[0], _nbcol * sizeof(int));

            //on stocke les donnees du fichier dans le tableau resultat
            fichier.read((char*)&tab[0], _nbcol * sizeof(int));

            fichier.close();
            return true;
        }
        //la ligne demandee nest pas valide
        else
        {
            cout<<"ligne non presente dans le fichier"<<endl;
        }
        fichier.close();
    }
    //en cas dechec douverture du fichier
    else
        cout<<"echec de lecture"<<endl;
    return false;
}

bool FileManager::addLine(int *tab)
{

    //on ouvre en lecture ecriture en fin de fichier
    fstream fichier(Box::nomPt, ios::in | ios::out | ios::ate |std::ifstream::binary);
    //verifie si le fichier demande a ete trouve
    if(fichier)
    {
        fichier.write((char*)&tab[0], _nbcol * sizeof(int));
        _lignes++;fichier.close();
        return true;

    }

    //en cas dechec douverture du fichier
    else
        cout<<"echec de lecture"<<endl;
    return false;
}

bool FileManager::switchVal(std::map<int, int> &mymap)
{
    ifstream fichier(Box::nomPt, ifstream::in| std::ifstream::binary);
    if(fichier)
    {
        ofstream out;//creation du fichier vide tmp
        string filenames = "tmp";
        out.open(filenames.c_str(),ios_base::out | ios_base::trunc);
        out.close();

        fstream fichiertmp(Box::nomTmp, ios::in | ios::out| std::ifstream::binary);
        if(fichiertmp)
        {
            lolilol tip;
            tip.a=0; tip.b=0;
            fichier.read((char*)&tip, sizeof(tip));
            fichiertmp.write((char*)&tip , sizeof(tip));

            map<int,int>::iterator it;
            int tab2[_nbcol];

            for(unsigned int i = 0; i < tip.b and !fichier.eof(); i++)
            {
                fichier.read((char*)&tab2, _nbcol * sizeof(int));
                for(unsigned int j = 0; j < tip.a; j++)
                {

                    it=mymap.find(tab2[j]);
                    if(it!= mymap.end())
                    {
                        tab2[j] = mymap[tab2[j]];
                    }
                }fichiertmp.write((char*)&tab2[0] , _nbcol * sizeof(int));
            }
            fichier.close();
            fichiertmp.close();

            string cmd = "del " + (string)Box::nomPt;
            system(cmd.c_str());
            cmd="rename tmp "+(string)Box::nomPt;
            system(cmd.c_str());
            mymap.clear();
            return true;
        }
        else
        cout<<"echec decriture lol"<<endl;
    }
    //en cas dechec douverture du fichier
    else
        cout<<"echec de lecture"<<endl;
    return false;
}


void FileManager::translate(bool b, string binary, string mnt)
{

    FILE * fichier;
    int a,x,y;
    string str1 = "rb";
    if(b)
    {
        ofstream out;//creation du fichier vide tmp
        out.open(mnt.c_str(),ios_base::out | ios_base::trunc);
        out.close();
        ifstream fichier(binary.c_str(), ios::in| std::ifstream::binary);

        fstream fichierb(mnt.c_str() , ios::in | ios::out);
        if(fichier)
        {
            if(fichierb.is_open())
            {
                fichier.read((char*)&x,sizeof(int));
                fichier.read((char*)&y,sizeof(int));
                fichierb<<x<<endl;
                fichierb<<y<<endl;
                for(int i = 0; i < 4; i++)
                {
                    a = 0;
                    fichier.read((char*)&a,sizeof(int));
                    fichierb<<a<<endl;
                }
                for(int i = 0; i < x; i++)
                {
                    for(int j = 0; j < y; j++)
                    {
                        a = 0;
                        fichier.read((char*)&a,sizeof(int));
                        fichierb<<a;
                        if(j!=(y-1)){fichierb<<" ";}
                    }
                    fichierb<<endl;
                 }
                fichier.close();
                fichierb.close();
            }
        }
        else cout<<"translate : impossible d'ouvrir "<<binary<<endl;
        fichier.close();
        fichierb.close();
    }
    else
    {
        ofstream out;
        out.open(binary.c_str(),ios_base::out | ios_base::trunc);
        out.close();

        fichier = fopen(binary.c_str(),"wb");
        ifstream fichierb(mnt.c_str() , ios::in);
        if(fichier)
        {
            fichierb>>x>>y;
            fwrite( &x , sizeof(int) , 1 , fichier);
            fwrite( &y , sizeof(int) , 1 , fichier);
            for(int i = 0; i < 4; i++)
            {
                fichierb>>a;
                fwrite( &a , sizeof(int) , 1 , fichier);
            }
            for(int i = 0; i < x; i++)
            {
                for(int j = 0; j < y; j++)
                {
                    fichierb>>a;
                    fwrite( &a , sizeof(int) , 1 , fichier);
                }
            }
        }
        else cout<<"translate : impossible d'ouvrir "<<mnt<<endl;
        fclose(fichier);
        fichierb.close();
    }
}

bool FileManager::getNextLine( int *tab)
{

    //verifie si le fichier demande a ete trouve
    if(fichiersource)
    {
        unsigned int cols,rows;
        rows = _nblig;
        cols = _nbcol;

        //verifie si on ne demande pas une ligne exterieure au fichiersource
        if(_nblig  > _lignes)
        {
            fichiersource->read((char*)&tab[0] , (_nbcol) * sizeof(int));
            return true;
        }
        //la ligne demandee nest pas valide
        else
        {
            cout<<"ligne non presente dans le fichiersource"<<endl;
        }
    }
    //en cas dechec douverture du fichier
    else
        cout<<"echec de lecture"<<endl;
    return false;
}


void FileManager::controle(string a, string b)
{
        ifstream fichierA(a.c_str(), ios::in| ios::binary);
        ifstream fichierB(b.c_str(), ios::in| ios::binary);
        lolilol top;
        unsigned int xa, xb, ya, yb;
        xa=0; ya = 0;
        top.a=0;top.b=0;
        fichierA.read((char*)&top ,sizeof(top));
        xa=top.a;
        ya=top.b;
        fichierB.read((char*)&top ,sizeof(top));
        xb=top.a;
        yb=top.b;
        int aa = 0, ab = 0;
        if(xa==xb && ya==yb)
        {
            int cpt = 0, res = 0;
            int tabaa[ya], tabab[ya],tabba[ya],tabbb[ya];
            fichierA.read((char*)&tabaa ,ya*sizeof(int));
            fichierB.read((char*)&tabba ,ya*sizeof(int));
            for(unsigned int i =0; i < (xa-1);i++)
            {
                fichierA.read((char*)&tabab ,ya*sizeof(int));
                fichierB.read((char*)&tabbb ,ya*sizeof(int));
                for(unsigned int j =0; j < (ya-1);j++)
                {
                    cpt+=2;
                    if((tabaa[j] == tabaa[j+1] && tabba[j] == tabba[j+1])||
                        (tabaa[j] != tabaa[j+1] && tabba[j] != tabba[j+1]))res++;
                    else aa++;
                    if((tabaa[j] == tabab[j] && tabba[j] == tabbb[j])||
                        (tabaa[j] != tabab[j] && tabba[j] != tabbb[j]))res++;
                    else ab++;
                }
                if(i != xa-2)
                {
                    for(unsigned int j = 0; j < ya;j++)
                    {
                        tabaa[j]=tabab[j];
                        tabba[j]=tabbb[j];
                    }
                }
            }
            double aff = (double)res / (double)cpt;
            cout<<"le resultat est juste a "<<aff*100<<"%"<<endl;
        }
        else cout<<"difference d'entete : "<<xa<<"&"<<xb<<"  "<<ya<<"&"<<yb<<endl;

        fichierA.close();
        fichierB.close();
}

void FileManager::majmap(map<int,int> &mymap)
{
    map<int,int>::iterator itb;
    bool ok = false;
    while (!ok)
    {
        ok = true;
        for(map<int,int>::iterator it = mymap.begin(); it !=mymap.end(); it++)
        {
            itb=mymap.find((*it).second);
            if(itb != mymap.end())
            {
                mymap[(*it).first]=(*itb).second;
                ok = false;
            }
        }
    }
}

void FileManager::stockMap(map<int,int> & mymap)
{
    //on ouvre en lecture ecriture en fin de fichier
    fstream fichier("defrag", ios::in | ios::out | ios::ate |std::ifstream::binary);
    //verifie si le fichier demande a ete trouve
    if(fichier)
    {
        for(map<int,int>::iterator it=mymap.begin(); it != mymap.end(); it++)
        {
            fichier.write((char*)&(*it).first,sizeof(int));
            fichier.write((char*)&(*it).second,sizeof(int));
        }
        fichier.close();
        mymap.clear();
    }
    else cout<<"IMPOSSIBLE "<<endl;
}

/*lecture
fichiersource = new std::ifstream("bassin.bin", std::ifstream::in | std::ifstream::binary);
    lolilol tip;
    fichiersource->read((char*)&tip, sizeof(tip));


    ecriture
    fstream fichier("degrag", ios::in | ios::out | ios::ate |std::ifstream::binary);
    fichier.write((char*)&(*it).first,sizeof(int));*/

void FileManager::defragmap()
{
    int cpt = cptfrag;
    int cpx = 0;
    ofstream out;
    out.open("tempo",ios_base::out | ios_base::trunc);
    out.close();
    fstream final;
    bool ok = false;
    while(!ok)
    {
        final.open("defragfinal", ios::in | ios::out | ios::ate |std::ifstream::binary);
        map<int,int> mapA;
        map<int,int> mapB;mapA.clear();mapB.clear();
        map<int,int> mapC;mapC.clear();
        map<int,int> mapD;mapD.clear();
        int a; int b;
        ok = true;
        while(cptfrag>0)
        {
            cpx = 0;
            ifstream source("defrag", std::ifstream::in | std::ifstream::binary);
            fstream tempo("tempo", ios::in | ios::out | ios::ate |std::ifstream::binary);


            for(unsigned int i = 0; i < 250000 && cpx < cptfrag; i++)
            {
                //je rempli la mapA a 50 000
                source.read((char*)&a, sizeof(int));
                source.read((char*)&b, sizeof(int));
                //cout<<a<<" "<<b<<endl;
                mapA[a]=b;
                mapC[a]=b;
                cpx++;
            }
            //cout<<5<<" "<<mapA[5]<<endl;
            while(cpx < cptfrag)
            {
                //cout<<"bouhahaha"<<endl;
                for(unsigned int i = 0; i < 250000 && cpx < cptfrag; i++)
                {
                    //je templi la mapB a 50 000
                    source.read((char*)&a, sizeof(int));
                    source.read((char*)&b, sizeof(int));
                    mapB[a]=b;
                    cpx++;
                }
                //si on est la mapA fait au moins 50000 cases
                //je met a jour tout les mapA en fonction des mapB & vice versa
                map<int,int>::iterator itb;
                for(map<int,int>::iterator it=mapA.begin();it!=mapA.end();it++)
                {
                    itb=mapB.find((*it).second);
                    if(itb != mapA.end())
                    {
                        //mapB.erase(it);
                        ok = false;
                        mapC[(*it).first]=(*itb).second;
                    }
                }
              //  cout<<mapC[a]<<" "<<mapC[b]<<endl;
                for(map<int,int>::iterator it=mapB.begin();it!=mapB.end();it++)
                {
                    itb=mapC.find((*it).second);
                    itb=mapA.find((*it).second);
                    if(itb != mapB.end())
                    {
                        //mapA.erase(it);
                        ok = false;
                        mapD[(*it).first]=(*itb).second;
                    }
                }
                //je recopie tout les mapB dans tempo
                for(map<int,int>::iterator it=mapB.begin(); it != mapB.end(); it++)
                //for(map<int,int>::iterator it=mapD.begin(); it != mapD.end(); it++)
                {
                    tempo.write((char*)&(*it).first,sizeof(int));
                    tempo.write((char*)&(*it).second,sizeof(int));
                }
                mapB.clear();
                mapD.clear();
            }
            //cout<<5<<" "<<mapC[5]<<endl;
            //if(mapC.size() < 2 )
            //{
                map<int,int>::iterator itb;
                for(map<int,int>::iterator it=mapA.begin();it!=mapA.end();it++)
                {
                    itb=mapB.find((*it).second);
                    if(itb != mapB.end())
                    {
              //          cout<<"VU"<<endl;
                        //mapB.erase(it);
                        ok = false;
                        mapC[(*it).first]=(*itb).second;
                    }
                }
            //}
            //cout<<5<<"aaaa"<<mapC[5]<<endl;

            //une fois au bout jecris mapA dans defagfinal
            for(map<int,int>::iterator it=mapC.begin(); it != mapC.end(); it++)
            {
                a = (*it).first; b = (*it).second;
             //   cout<<a<<"xxxxxxxxxxx"<<b<<endl;
                final.write((char*)&a,sizeof(int));
                final.write((char*)&b,sizeof(int));
                cptfrag--;
            }
            mapA.clear();
            mapC.clear();
            //tempo ecrase defrag et on recommence
            tempo.close();
            source.close();
            string cmd = "del defrag";
            system(cmd.c_str());
            cmd="rename tempo defrag";
            system(cmd.c_str());
            out.open("tempo",ios_base::out | ios_base::trunc);
            out.close();
        }
        if(ok)
        {
            //cout<<"troll"<<endl;
            for(map<int,int>::iterator it=mapC.begin(); it != mapC.end(); it++)
            {
                a = (*it).first; b = (*it).second;
             //   cout<<a<<"mmm"<<b<<endl;
                final.write((char*)&a,sizeof(int));
                final.write((char*)&b,sizeof(int));
                cptfrag--;
            }
        }
        final.close();
        if(!ok)
        {
            out.open("tmp2",std::ios_base::out | std::ios_base::trunc);
            out.close();
            final.open("tmp2", ios::in | ios::out | ios::ate |std::ifstream::binary);
            ifstream source("defragfinal", std::ios::in | std::ifstream::binary);
            int a;int b=0;
            while(b < (cpt*2))
            {
                source.read((char*)&a, sizeof(int));
                //cout<<a<<" ";
                final.write((char*)&a,sizeof(int));
                b++;
            }
            //cout<<endl;
            final.close();
            source.close();

            string cmd = "del defrag";
            system(cmd.c_str());
            cmd="rename tmp2 defrag";
            system(cmd.c_str());
            out.open("defragfinal",ios_base::out | ios_base::trunc);
            out.close();
        }
    }
    //defragfinal ne contient que des map qui se sont mises a jour
    cout<<"defragmentation terminee. Debut MAJ des bassins"<<endl;
    ifstream source("defragfinal", std::ifstream::in | std::ifstream::binary);
    int a; int b;
    map<int,int> mapC;
    int val = cpt; int compteur = 0;
    while(cpt > 0)
    {
        if( (double)val / (double)cpt > compteur)
        {
            compteur++;
            cout<<compteur<<"%"<<endl;
        }
        for(unsigned int i = 0; i <400000 && cpt > 0; i++)
        {
            source.read((char*)&a, sizeof(int));
            source.read((char*)&b, sizeof(int));
            mapC[a]=b;
           // cout<<a<<" "<<b<<endl;
            cpt--;
        }
        switchVal(mapC);
        mapC.clear();
    }
    cout<<"bassins mis a jour"<<endl;
    source.close();
}
